/*
 * SecurityPolicy.java
 *
 * Created on August 3, 2006, 4:38 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package org.glossitope.container.security;

import org.glossitope.container.security.permissions.SecurityStorage;
import java.io.File;
import java.io.FilePermission;
import java.net.SocketPermission;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.logging.Logger;

import org.glossitope.container.Core;
import org.glossitope.container.Environment;
import org.glossitope.container.PermissionManager;
import org.glossitope.container.security.permissions.GPerm;
import org.glossitope.container.util.StringUtils;
import org.joshy.u;


/**
 *
 * @author cooper
 */
public class SecurityPolicy extends Policy {
    
    private static final Logger LOG = Logger.getLogger("org.glossitope");
    private static final File SECURITY_PROPS = new File( Environment.HOME, "security.properties");
    private static final ArrayList<String> SAFE_RUNTIME = new ArrayList<String>();
    private static final ArrayList<String> SAFE_ACCESS_CLASS_IN_PACKAGE = new ArrayList<String>();

    
    static{
        SAFE_RUNTIME.add( "modifyThread");
        SAFE_RUNTIME.add( "modifyThreadGroup");
        SAFE_RUNTIME.add( "stopThread");
        SAFE_RUNTIME.add( "getClassLoader");
        SAFE_RUNTIME.add( "createClassLoader");
        SAFE_RUNTIME.add( "accessClassInPackage");
        SAFE_ACCESS_CLASS_IN_PACKAGE.add( "sun.util.logging.resources");
    }
    
    private Hashtable<CodeSource, Permissions> permissions = new Hashtable<CodeSource, Permissions>();
    
    private Core core;
    
    /** Creates a new instance of SecurityPolicy */
    public SecurityPolicy(Core core) {
        super();
        System.out.println("========== Security Policy Created ============");
        this.core = core;
        SecurityStorage.getInstance();
    }
    
    public PermissionCollection getPermissions(CodeSource codeSource) {
        u.p("SP: getPermissions called: " + codeSource);
        Permissions p = permissions.get(codeSource);
        
        if(p == null) {
            p = new Permissions();
            permissions.put(codeSource, p);
        }
        
        return p;
    }
    
    
    public void preauthorize(String uuid, GPerm perm) {
        SecurityStorage.getInstance().grantPermissionPermanently(uuid, perm);
    }
    
    public boolean implies(ProtectionDomain protectionDomain,
            Permission permission) {
        //auto-grant all
        if(true) return true;
        if(Environment.autoGrantAll) { return true; }
        
        //check for certain things that are known to be safe
        if(permission instanceof java.awt.AWTPermission ||
                permission instanceof java.util.PropertyPermission ||
                (permission instanceof java.lang.RuntimePermission && SAFE_RUNTIME.contains( permission.getName() )  ) ) {
            return true;
        }
        
        System.out.println("requesting: " + permission);// + " from " + protectionDomain);
        //check for things that are known to be safe
        if(permission instanceof java.lang.RuntimePermission) {
            if(permission.getName().startsWith("accessClassInPackage")) {
                String clss = permission.getName().substring("accessClassInPackage".length()+1);
                if(SAFE_ACCESS_CLASS_IN_PACKAGE.contains(clss)) {
                    return true;
                }
            }
        }
        //System.out.println("perm = " + permission.getClass().getName() + " " + permission.getName() + " action = " + permission.getActions());
        /*
        System.out.println(protectionDomain.getCodeSource().getLocation() +
                " requesting permission " + permission.getClass().getName() + " " +
                permission.getName());*/
        
        //this should indicate that whatever is calling us is part of the admin and is trusted
        if(permission instanceof org.glossitope.container.security.DeskletAdministrationPermission) {
            return (protectionDomain.getClassLoader() == SecurityPolicy.class.getClassLoader());
        }
        
        if(protectionDomain.getClassLoader() instanceof ClassLoader) {
            ClassLoader loader = (ClassLoader) protectionDomain.getClassLoader();
            Permissions perms = permissions.get(protectionDomain.getCodeSource());
            
            if(perms == null) {
                perms = new Permissions();
            }
            
            if(perms.implies(permission)) {
                return true;
            }
            
            //pre-approve the env home. josh: is this correct?
            //does this let widgets access eachother
            if(permission instanceof FilePermission && (Environment.HOME != null) &&
                    (permission.getName()
                    .startsWith(Environment.HOME.getAbsolutePath()) ||
                    permission.getName()
                    .startsWith(System.getProperty(
                    "java.io.tmpdir")))) {
                perms.add(permission);
                
                return true;
            }
            
            //josh: this line appears to be clobbered by the next line
            String permissionValue = permission instanceof java.net.SocketPermission ? "java.net.SocketPermission" : permission.getName();
            permissionValue = permission instanceof java.io.FilePermission ? "java.io.FilePermission" : permission.getName();
            
            
            //pre-authorization stuff
            String externUrl = protectionDomain.getCodeSource()
                    .getLocation()
                    .toExternalForm();
            String uuid = StringUtils.substring(externUrl, "glossitope/(.+?)/");
            
            //always allow DNS resoluation
            if(permission instanceof java.net.SocketPermission) {
                SocketPermission sockp = (SocketPermission) permission;
                if("resolve".equals(sockp.getActions())) {
                    u.p("SP: preapproving resolutions");
                    return true;
                }
            }
            
            
            if(SecurityStorage.getInstance().isGranted(uuid, permission)) {
                return true;
            }
            
            
            
            StringBuffer sb = new StringBuffer();
            sb.append(loader.getName());
            sb.append(" has asked for permission " + permission.getName());
            PermissionManager.Permission value = core.getPermissionManager().requestPermission(sb.toString());
            
            switch(value) {
            case YES:
                perms.add(permission);
                permissions.put(protectionDomain.getCodeSource(), perms);
                SecurityStorage.getInstance().grantPermission(protectionDomain.getCodeSource(), permission);
                return true;
                
            case ALWAYS:
                SecurityStorage.getInstance().grantPermissionPermanently(protectionDomain.getCodeSource(), permission);
                return true;
                
            case NEVER:
                SecurityStorage.getInstance().denyPermissionPermanently(protectionDomain.getCodeSource(), permission);
                return false;
                
            default:
                return false;
            }
        } else {
            return true;
        }
    }
    
    
    public void refresh() {
    }
}
